//
// Created by QU on 2023/8/17.
//

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>

#include "dataGeneratorFiller.h"
#include "header.h"
#include "utils.h"

size_t fillBlockWithZero(uint8_t *mallocBuffer, size_t filledSize) {
    if (mallocBuffer == NULL || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }

    memset(mallocBuffer, 0x00, filledSize);
    return filledSize;
}

size_t fillBlockWithRandom(uint8_t *mallocBuffer, size_t filledSize) {
    srand(time(NULL));

    if (mallocBuffer == NULL || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }

    for (size_t i = 0; i < filledSize; i++) {
        mallocBuffer[i] = rand() % 0xFF;
    }

    // Return the filledSize
    return filledSize;
}

size_t fillBlockWithSpecificBlock(uint8_t *mallocBuffer, size_t bufferSize, size_t filledSize,
                                  const uint8_t *specificData, size_t specificDataSize) {
    if (mallocBuffer == NULL || bufferSize <= 0 || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    if (filledSize > bufferSize) {
        printWarningMessage("Filled size is bigger than buffer size");
        return 0;
    }

    if (specificDataSize > filledSize) {
        printWarningMessage("Specific data size is bigger than filled size");
        return 0;
    }

    size_t currFilledSize = 0;
    while (currFilledSize < filledSize) {
        mallocBuffer[currFilledSize] = specificData[currFilledSize % specificDataSize];
        currFilledSize++;
    }

    return currFilledSize;
}


void generateHeader(vcTypeHeader_t *target, enum VC_TYPE vcType, uint16_t logicalChannelID,
                    uint16_t physicalChannelID, uint16_t SQ, size_t frameLength, uint16_t frameNum) {
    target->logicalChannelID = logicalChannelID;
    target->physicalChannelID = physicalChannelID;
    target->SQ = SQ;
    target->vc_type = vcType;
    target->frameLength = frameLength;
    target->frameNum = frameNum;
}


size_t generateData_60a1da8e(uint8_t *mallocBuffer, size_t bufferSize, size_t filledSize) {
    if (mallocBuffer == NULL || bufferSize <= 0 || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    if (filledSize > bufferSize) {
        printWarningMessage("Filled size is bigger than buffer size");
        exit(EXIT_FAILURE);
    }

    // little endian
    uint8_t specificBlock[4] = {0x8E, 0xDA, 0xA1, 0x60};

    return fillBlockWithSpecificBlock(mallocBuffer, bufferSize, filledSize, specificBlock, 4);
}

size_t generateData_00toFF(uint8_t *mallocBuffer, size_t bufferSize, size_t filledSize) {
    if (mallocBuffer == NULL || bufferSize <= 0 || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    if (filledSize > bufferSize) {
        printWarningMessage("Filled size is bigger than buffer size");
        return 0;
    }

    uint8_t currData = 0x00;
    size_t filledSizeCounter = 0;

    while (filledSizeCounter < filledSize) {
        mallocBuffer[filledSizeCounter] = currData;
        filledSizeCounter++;
        currData++;
    }
    return filledSizeCounter;
}

size_t generateData_LowerIncrementHigherLogicalID(uint8_t *mallocBuffer, size_t bufferSize, size_t filledSize,
                                                  uint16_t logicalID) {
    if (mallocBuffer == NULL || bufferSize <= 0 || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    if (filledSize > bufferSize) {
        printWarningMessage("Filled size is bigger than buffer size");
        return 0;
    }

    size_t currFilledSize = 0;

    uint16_t lowerBytes = 0;
    uint16_t higherBytes = logicalID;
    // 如果整除4字节的部分
    for (size_t i = 0; i < filledSize; i += 4) {

        // Store in Little Endian
        mallocBuffer[i] = (uint8_t) (lowerBytes & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 1] = (uint8_t) ((lowerBytes >> 8) & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 2] = (uint8_t) (higherBytes & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 3] = (uint8_t) ((higherBytes >> 8) & 0xFF);
        currFilledSize++;

        lowerBytes++;
    }

    // 后续不足4字节部分

    return currFilledSize;
}

size_t
generateData_ChannelIncrement(uint8_t *mallocBuffer, size_t bufferSize, size_t filledSize, uint16_t logicalID) {
    if (mallocBuffer == NULL || bufferSize <= 0 || filledSize <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    if (filledSize > bufferSize) {
        printWarningMessage("Filled size is bigger than buffer size");
        return 0;
    }

    size_t currFilledSize = 0;

    uint32_t pattern = logicalID;           // 4 bytes;
    // 如果整除4字节的部分
    for (size_t i = 0; i < filledSize; i += 4) {
        // Store in Little Endian
        mallocBuffer[i] = (uint8_t) (pattern & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 1] = (uint8_t) ((pattern >> 8) & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 2] = (uint8_t) ((pattern >> 16) & 0xFF);
        currFilledSize++;
        mallocBuffer[i + 3] = (uint8_t) ((pattern >> 24) & 0xFF);
        currFilledSize++;

        pattern += logicalID;
    }
    // 后续不足4字节部分

    return currFilledSize;
}

size_t outputBufferLoadFiller(const uint8_t *buffer, size_t bufferLength, enum VC_TYPE vcType, uint16_t splitedCols,
                              size_t outBufferOneLineLength, uint8_t outBuffer[splitedCols][outBufferOneLineLength]) {
    if (buffer == NULL || bufferLength <= 0 || splitedCols <= 0 || outBuffer == NULL) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }

    size_t headerSize = VC_FRAME_HEADER_LENGTH;

    size_t bodySize;
    switch (vcType) {
        case VC_4:
            bodySize = VC4_FRAME_BODY_LENGTH;
            break;
        case VC_12:
            bodySize = VC12_FRAME_BODY_LENGTH;
            break;
        default:
            printErrorMessage("VC type is not supported");
            exit(EXIT_FAILURE);
    }

    size_t oneLineFrameNum = getFrameNumOneLineFromBufferLength(bufferLength, vcType, splitedCols);

    // 这个部分填充的是LOAD.
    size_t originBufferSeek = 0;
    for (size_t currLineFrameNum = 0; currLineFrameNum < oneLineFrameNum; currLineFrameNum++) { // lineFrameNum作为定位计数使用.
        for (size_t bytes = headerSize + sizeof(uint16_t);
             bytes < headerSize + bodySize; bytes++) { // 针对每个字节, HEADER + MFI作为写入开始,
            for (size_t sq = 0; sq < splitedCols; sq++) { // 遍历SQ放入.
                if (originBufferSeek < bufferLength) {
                    outBuffer[sq][bytes + bodySize * currLineFrameNum] = buffer[originBufferSeek];
                    originBufferSeek++;
                } else {
                    break; // 如果填充完毕, 后面的原始数据不动
                }
            }
        }
    }

    return originBufferSeek;
}

size_t
outputBufferHeaderFillerWithIndex(vcTypeHeader_t *headerData, uint16_t splitedCols, size_t inputBufferOneLineLength,
                                  uint8_t inputBuffer[splitedCols][inputBufferOneLineLength],
                                  size_t targetColFirstFrameIndex) {
    if (inputBuffer == NULL || inputBufferOneLineLength <= 0 || splitedCols <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }

    if (targetColFirstFrameIndex > splitedCols - 1) {
        printErrorMessage("targetHeaderIndex out of bounds");
        exit(EXIT_FAILURE);
    }

    uint8_t headerDataArr[sizeof(vcTypeHeader_t)];
    // HEADER的数据变成字节数组的形式
    memcpy(headerDataArr, headerData, sizeof(vcTypeHeader_t));

    size_t filledBytes = 0;
    for (size_t currByte = 0; currByte < VC_FRAME_HEADER_LENGTH; ++currByte) {
        inputBuffer[targetColFirstFrameIndex][currByte] = headerDataArr[currByte];
        filledBytes++;
    }
    return filledBytes;
}


size_t outputBufferHeaderFiller(vcTypeHeader_t startHeader, enum VC_TYPE vcType, uint16_t splitedCols,
                                size_t inputBufferOneLineLength,
                                uint8_t inputBuffer[splitedCols][inputBufferOneLineLength]) {

    if (inputBuffer == NULL || inputBufferOneLineLength <= 0 || splitedCols <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }
    size_t filledBytes = 0;
    size_t currFilledBytes = 0;

    vcTypeHeader_t patternHeader = startHeader;

    size_t frameLen;
    switch (vcType) {
        case VC_4:
            frameLen = VC4_FRAME_FULL_LENGTH;
            break;
        case VC_12:
            frameLen = VC12_FRAME_FULL_LENGTH;
            break;
        default:
            printErrorMessage("VC type is not supported");
            exit(EXIT_FAILURE);
    }

    uint16_t frameNum = getFrameNumOneLineFromOneLineLength(vcType, inputBufferOneLineLength);

    // 填充部分
    size_t totalColNum = splitedCols;

    for (size_t currCol = 0; currCol < totalColNum; ++currCol) {
        // logicalID 不变
        // physicalID 加分裂行数
        patternHeader.physicalChannelID = startHeader.physicalChannelID + currCol;

        patternHeader.SQ = currCol; // 有几行就是 0到几

        // TODO: 这里可能出现frameNum不能容纳一行的Length的情况.  暂时 % 4096 不让他溢出好了
        patternHeader.frameNum = frameNum;

        patternHeader.frameLength = frameLen;

        // TODO: vcType. 冗余单位.
        patternHeader.vc_type = vcType;


        currFilledBytes = outputBufferHeaderFillerWithIndex(&patternHeader, splitedCols, inputBufferOneLineLength,
                                                            inputBuffer,
                                                            currCol);
        filledBytes += currFilledBytes;
    }
    return filledBytes;
}

size_t outputBufferMFIFiller(enum VC_TYPE vcType, uint16_t splitedCols,
                             size_t inputBufferOneLineLength,
                             uint8_t inputBuffer[splitedCols][inputBufferOneLineLength], uint16_t startMFI) {
    if (inputBuffer == NULL || inputBufferOneLineLength <= 0 || splitedCols <= 0) {
        printErrorMessage("Parameter Illegal");
        exit(EXIT_FAILURE);
    }

    size_t bodySize;
    switch (vcType) {
        case VC_4:
            bodySize = VC4_FRAME_BODY_LENGTH;
            break;
        case VC_12:
            bodySize = VC12_FRAME_BODY_LENGTH;
            break;
        default:
            printErrorMessage("VC type is not supported");
            exit(EXIT_FAILURE);
    }

    uint16_t currMFI = startMFI;
    uint8_t tempMFI[sizeof(startMFI)];

    size_t filledBytes = 0;

    size_t oneLineFrameNum = getFrameNumOneLineFromOneLineLength(vcType, inputBufferOneLineLength);
    for (size_t currLineFrameNum = 0; currLineFrameNum < oneLineFrameNum; ++currLineFrameNum) {
        for (uint16_t currCol = 0; currCol < splitedCols; ++currCol) {
            for (size_t bytes = 0; bytes < sizeof(startMFI); ++bytes) {
                memcpy(&tempMFI, &currMFI, sizeof(startMFI));
                inputBuffer[currCol][bytes + (bodySize * currLineFrameNum) + VC_FRAME_HEADER_LENGTH] = tempMFI[bytes];
                filledBytes++;
            }
        }
        currMFI++, currMFI %= 4096;
    }
    return filledBytes;
}


bool
outputToFiles(uint16_t splitedCols, size_t inputBufferOneLineLength,
              uint8_t inputBuffer[splitedCols][inputBufferOneLineLength],
              uint16_t startPhysicalID) {

    // 假设有5个文件分别为 file1.txt, file2.txt, file3.txt, file4.txt, file5.txt
    // 将这5个文件依次打开，并将每个文件的内容分别存入 inputBuffer 中对应行的所有字节
    for (size_t currCol = 0; currCol < splitedCols; currCol++) {
        char filename[20];
        sprintf(filename, "Data_PhysicalChannel_%u.dat", startPhysicalID); // 生成文件名，例如：file1.txt, file2.txt, ...

        FILE *file = fopen(filename, "w+b");
        if (file == NULL) {
            printErrorMessage("Opening File ERROR.");
            printf("opening file: %s ERROR.\n", filename);
            return false;
        }

        // 将 inputBuffer 的第 currCol 行读取到文件内容中
        size_t bytesFilled = fwrite(inputBuffer[currCol], sizeof(char), inputBufferOneLineLength, file);
        if (VERBOSE_FLAG) {
            printf("File: %s, filled Bytes %zu.\n", filename, bytesFilled);
        }
        startPhysicalID++;

        fclose(file);
    }

    return true;
}

#ifdef TEST_MODE

void testDataGenerator() {
    printDebugMessageWithExtraMessage("------------------------------------------ testDataGenerator ------------------------------------------", "");

    if (VERBOSE_FLAG) {

        // first input you wanna to generate FrameNumber, it will be converted to bufferLength.
        enum VC_TYPE defVCType = VC_12;
        size_t targetFrameNum = 2;
        size_t defSplitCols = 2;
        uint16_t defLogicalID = 2;
        uint16_t defPhysicalID = 1;
        uint16_t defSQ = 0;
        uint16_t defStartMFI = 10;

        vcTypeHeader_t defHeader;
        generateHeader(&defHeader, defVCType, defLogicalID, defPhysicalID, defSQ, 0, 0);

        printf("defVCType: %s\n", defVCType == VC_4 ? "VC_4" : defVCType == VC_12 ? "VC_12" : "UNKNOWN VC_TYPE");
        printf("defSplitCols: %zu\n", defSplitCols);
        printf("targetFrameNum: %zu\n", targetFrameNum);
        printf("defLogicalID: %u\n", defLogicalID);
        printf("\n");

        size_t targetBufferLength = getBufferLengthFromGenFrameNum(targetFrameNum, defVCType);
        printf("targetBufferLength: %zu\n", targetBufferLength);
        printf("targetTotalFullLengthOneLine: %zu\n",
               getTotalFullLengthOneLineFromBufferLength(targetBufferLength, defVCType, defSplitCols));
        printf("targetTotalFullLength: %zu\n",
               getTotalFullLengthFormBufferLength(targetBufferLength, defVCType, defSplitCols));
        printf("\n");

        // malloc data block
        uint8_t *generateBuffer = malloc(targetBufferLength);
        if (generateBuffer == NULL) {
            printErrorMessage("malloc buffer failed");
        } else {
            printInfoMessage("malloc buffer success");
        }

        // test filler
        printDebugMessageWithExtraMessage("----\t test filler \t----", "");
        printInfoMessage("test fillBlockWithZero");
        fillBlockWithZero(generateBuffer, targetBufferLength);
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);

        printInfoMessage("test fillBlockWithSpecificBlock");
        uint8_t specificBlock[4] = {0x01, 0x02, 0x03, 0x04};
        fillBlockWithSpecificBlock(generateBuffer, targetBufferLength, targetBufferLength, specificBlock, 4);
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);

        printInfoMessage("test fillBlockWithRandomBlock");
        fillBlockWithRandom(generateBuffer, targetBufferLength);
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);
        printDebugMessageWithExtraMessage("----\t test filler complete \t----", "");

        // test generator
        printDebugMessageWithExtraMessage("----\t test generator \t----", "");
        generateData_60a1da8e(generateBuffer, targetBufferLength, targetBufferLength);
        printInfoMessage("generateData_60a1da8e start!");
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);

        generateData_00toFF(generateBuffer, targetBufferLength, targetBufferLength);
        printInfoMessage("generateData_00toFF start!");
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);

        generateData_ChannelIncrement(generateBuffer, targetBufferLength, targetBufferLength, defLogicalID);
        printInfoMessage("generateData_ChannelIncrement start!");
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);

        generateData_LowerIncrementHigherLogicalID(generateBuffer, targetBufferLength, targetBufferLength,
                                                   defLogicalID);
        printInfoMessage("generateData_LowerIncrementHigherLogicalID start!");
        printDebugMessage("bufferDataContent:");
        printBufferDataContent(generateBuffer, targetBufferLength);
        printDebugMessageWithExtraMessage("----\t test generator complete \t----", "");

        /////////////

        // test outputBuffer
        printDebugMessageWithExtraMessage("----\t test outputBuffer \t----", "");
        size_t outBufferLength = getTotalFullLengthFormBufferLength(targetBufferLength, defVCType, defSplitCols);
        size_t outBufferOnelineLength = getTotalFullLengthOneLineFromBufferLength(targetBufferLength, defVCType,
                                                                                  defSplitCols);

        uint8_t *outputBuffer = malloc(outBufferLength);
        if (outputBuffer == NULL) {
            printErrorMessage("malloc buffer failed");
        } else {
            printInfoMessage("malloc buffer success");
        }

        printInfoMessage("printDefaultVCTypeHeader start!");
        printVCTypeHeader(&defHeader);

        printInfoMessage("outputBufferLoadFiller start!");
        outputBufferLoadFiller(generateBuffer, targetBufferLength, defVCType, defSplitCols, outBufferOnelineLength,
                               (void *) outputBuffer);

        printOutputBufferContent(defVCType, defSplitCols, outBufferOnelineLength, (void *) outputBuffer);
        printInfoMessage("outputBufferHeaderFiller start!");
        outputBufferHeaderFiller(defHeader, defVCType, defSplitCols, outBufferOnelineLength, (void *) outputBuffer);
        printOutputBufferContent(defVCType, defSplitCols, outBufferOnelineLength, (void *) outputBuffer);
        printInfoMessage("outputBufferMFIFiller start!");
        outputBufferMFIFiller(defVCType, defSplitCols, outBufferOnelineLength, (void *) outputBuffer, defStartMFI);
        printOutputBufferContent(defVCType, defSplitCols, outBufferOnelineLength, (void *) outputBuffer);


        // free buffer
        free(generateBuffer);
        printInfoMessage("free generateBuffer success");

        free(outputBuffer);
        printInfoMessage("free outputBuffer success");
        printDebugMessageWithExtraMessage("----\t test outputBuffer complete \t----", "");

        printDebugMessageWithExtraMessage("------------------------------------------ testDataGenerator complete ------------------------------------------",
                                          "\n");
    }
}


#endif
